/**********************************************
*  Classes:   GLRenderer
*  Desc:      Renders a scene graph using OpenGL.
*			  Singleton class
*  Author:    Vaios Kalpias-Ilias (C) 2008-2009
***********************************************/

#include "GLRenderer.h"
#include "GeometryNode.h"
#include "Swap.h"
#include "Quaternion.h"
#include "Helper.h"
#include "WindowManager.h"

using namespace boost;
//-----------------------------------------------
GLRenderer* GLRenderer::m_spInstance = 0;
//-----------------------------------------------
/**
* Ctor
*/
GLRenderer::GLRenderer() 
: m_bInitialized(false),
  m_bDrawBounds(false),
  m_uiMaxLights(8),
  m_uiSkyBoxDL(0),
  m_pWndMgr(WindowManager::GetInstance())
{
}
//-----------------------------------------------
/**
* Dtor
*/
GLRenderer::~GLRenderer()
{
	TextureList::iterator texIt;
	for (texIt = m_lTextures.begin();
		texIt != m_lTextures.end();
		++texIt)
	{
		glDeleteTextures(1, &*texIt);
	}
	m_lTextures.clear();
	
	if (m_bInitialized)
	{
		RenderList::iterator it;
		for (it = m_lRenderStructs.begin();
			it != m_lRenderStructs.end();
			++it)
		{
			unsigned int nBuffers[3] = 
			{ (*it)->verticesName, 
			  (*it)->normalsName,  
			  (*it)->texCoordsName};
			glDeleteBuffersARB( 3, nBuffers );

			delete (*it);
		}

		m_lRenderStructs.clear();
	}

	m_spCamera = CameraPtr();

	m_spSkyBox = SkyBoxPtr();

	if (m_uiSkyBoxDL)
	{
		glDeleteLists(m_uiSkyBoxDL, 1);
	}

	ClearSorted();
}
//-----------------------------------------------
/**
* Gets single instance
*/
GLRenderer* GLRenderer::GetInstance()
{
	return m_spInstance ? 
			m_spInstance :
			m_spInstance = new GLRenderer();
}
//-----------------------------------------------
/**
* Destroys single instance
*/
void GLRenderer::Destroy()
{
	if (m_spInstance)
	{
		delete m_spInstance;
		m_spInstance = 0;
	}
}
//-----------------------------------------------
/**
* Initializes renderer
*/
bool GLRenderer::Init()
{
	if (!IsVBOExtensionSupported()) return false;
	// VBO Function Pointers
	glDeleteBuffersARB = 
			(PFNGLDELETEBUFFERSARBPROC) wglGetProcAddress("glDeleteBuffersARB");

	glBindBufferARB = 
			((PFNGLBINDBUFFERARBPROC) wglGetProcAddress("glBindBufferARB"));

	glGenBuffersARB = 
			((PFNGLGENBUFFERSARBPROC) wglGetProcAddress("glGenBuffersARB"));

	glBufferDataARB = 
			((PFNGLBUFFERDATAARBPROC) wglGetProcAddress("glBufferDataARB"));

	m_spCamera = CameraPtr(new Camera);
	

	return m_bInitialized = true;
}
//-----------------------------------------------
/**
* Renders scene
*/
void GLRenderer::RenderScene(SceneNode *scene)
{
	if (!scene) return;

	// update camera
	if (!m_spCamera->GetParent())
		m_spCamera->Update();

	if (m_spCamera->UpdateProjectionFlag())
	{
		GLWindow* wnd = WindowManager::GetInstance()
			->GetActiveWindow();
		if (wnd)
		{
			wnd->Resize(wnd->GetWidth(), wnd->GetHeight());
		}
	}
	// Clear The Screen And The Depth Buffer
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);	
	
	// Reset The View
	glLoadIdentity();			
	
	// setup OpenGL camera
	const mth::Vector3& pos    = m_spCamera->GetGlobalTranslation();
	const mth::Vector3& target = m_spCamera->GetTargetPos();
	const mth::Vector3& up = m_spCamera->GetGlobalUp();
	gluLookAt(pos.x, pos.y, pos.z,
		      target.x, target.y, target.z,
			  up.x, up.y, up.z);
			  
	// sort states
	SortScene(scene);

	// set the background color
	glClearColor(m_vBackgroundColor.x, 
		m_vBackgroundColor.y, 
		m_vBackgroundColor.z,
		1.0);
	
	// enable OpenGL stuff
	glShadeModel(GL_SMOOTH);
	glEnable(GL_NORMALIZE);
	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LEQUAL);
	glEnable(GL_CULL_FACE);
	glEnable(GL_BLEND);
	glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	// draw skybox
	if (m_spSkyBox)
	{
		if (m_spSkyBox->GetMoveWithCamera())
		{
			const mth::Vector3& pos = m_spCamera->GetGlobalTranslation();
			glPushMatrix();
			glTranslatef(pos.x, pos.y, pos.z);
			glCallList(m_uiSkyBoxDL);
			glPopMatrix();
		}
		else
		{
			glCallList(m_uiSkyBoxDL);
		}
	}

	// render sorted geometry
	RenderSorted();
	
	// disable all OpenGL states
	glDisable(GL_BLEND);
	glDisable(GL_CULL_FACE);
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_NORMALIZE);
	
	// clear sorted lists
	ClearSorted();
}
//-----------------------------------------------
/**
* Sorts states in scene
*/
void GLRenderer::SortScene(SceneNode *scene)
{
	if (scene->DerivesFromTransform())
	{
		if (!IsVisible(static_cast<TransformNode*>(scene)))
		{
			return;
		}

	}
	else
		return;

	// if this is a geometry node
	if (scene->DerivesFromGeometry())
	{
		// create a SortStruct and add it 
		GeometryNode* geom = static_cast<GeometryNode*>(scene);

		TexturePropertyPtr tex  = static_pointer_cast<TextureProperty>
			(geom->GetProperty(NodeProperty::PROPERTY_TEXTURE));


		MaterialPropertyPtr mat  = static_pointer_cast<MaterialProperty>
			(geom->GetProperty(NodeProperty::PROPERTY_MATERIAL));

		TextureProperty* texP = 0;
		if (tex)
		{
			texP = tex.get();
		}

		MaterialProperty* matP = 0;
		if (mat)
		{
			matP = mat.get();
		}
		TexturesMap::iterator texIt = m_SortedGeometry.find(texP);
		if ( texIt != m_SortedGeometry.end())
		{
			MaterialMap::iterator matIt = texIt->second->find(matP);
			if (matIt != texIt->second->end())
			{
				matIt->second->push_back(geom);
			}
			else
			{
				GeometryList* l = new GeometryList;
				l->push_back(geom);
				texIt->second->insert(make_pair(matP, l));
			}
		}
		else
		{
			GeometryList* l = new GeometryList;
			l->push_back(geom);
			MaterialMap* m = new MaterialMap;
			m->insert(make_pair(matP, l));
			m_SortedGeometry.insert(make_pair(texP, m));
		}
	}

	// do the same for the children
	const SceneNode::NodeList children = scene->GetChildren();
	SceneNode::NodeList::const_iterator it;
	for (it = children.begin();
		it != children.end();
		++it)
	{
		SortScene((*it).get());
	}
}
//----------------------------------------------
/**
* Renders sorted geometry
*/
void GLRenderer::RenderSorted()
{

	bool textureBinded = false;

	TexturesMap::iterator texIt = m_SortedGeometry.begin();
	for (; texIt != m_SortedGeometry.end(); ++texIt)
	{
		textureBinded = false;
		TextureProperty* texture = texIt->first;
		MaterialMap::iterator matIt = texIt->second->begin();
		for (; matIt != texIt->second->end(); ++matIt)
		{
			bool materialBinded = false;
			MaterialProperty* material = matIt->first;

			GeometryList::iterator geomIt = matIt->second->begin();
			for (; geomIt != matIt->second->end(); ++geomIt)
			{
				GeometryNode* node = *geomIt;
				EnableLights(node);
				EnableVertices(node);
				EnableNormals(node);
				EnableTexCoords(node);

				if (node->GetTexCoordsName())
				{
					if (texture)
					{			
						if (!textureBinded)
						{
							glEnable(GL_TEXTURE_2D);
							EnableTextures(texture);
							textureBinded = true;
						}			
						else
						{
							glEnable(GL_TEXTURE_2D);
						}

					}
				}

				// change material if necessary
				if (!materialBinded)
				{
					if (material)
					{			
						materialBinded = true;
						EnableMaterial(material);					
					}								
				}


				// set transform matrix
				glPushMatrix();

				// translate
				glTranslatef(node->GetGlobalTranslation().x, 
					node->GetGlobalTranslation().y,
					node->GetGlobalTranslation().z);

				// rotate
				float angle;
				mth::Vector3 axis;
				node->GetGlobalRotationQuat().GetAxisAngle(axis,angle);
				glRotatef(angle*mth::TO_DEG, axis.x, axis.y, axis.z); 

				// scale
				glScalef(node->GetScale().x, node->GetScale().y, node->GetScale().z);



				// Draw All Of The Triangles At Once
				glDrawArrays( GL_TRIANGLES, 0, node->GetVerticesCount() );	

				// restore matrix
				glPopMatrix();

				// disable textures
				if (textureBinded)
					glDisable(GL_TEXTURE_2D);

				DisableTexCoords(node);
				DisableNormals(node);
				DisableVertices(node);
				DisableLights();
			}
		}
	}

}
//----------------------------------------------
/**
* Clears sorted geometry map
*/
void GLRenderer::ClearSorted()
{
	TexturesMap::iterator it = m_SortedGeometry.begin();
	for (; it != m_SortedGeometry.end(); ++it)
	{
		MaterialMap::iterator mIt = it->second->begin();
		for(; mIt != it->second->end(); ++mIt)
		{
			mIt->second->clear();
			delete mIt->second;
		}

		it->second->clear();
		delete it->second;
	}

	m_SortedGeometry.clear();

}
//-----------------------------------------------
/**
* Enables vertices
*/
void GLRenderer::EnableVertices(GeometryNode* node)
{
	if (node->GetVerticesName())
	{
		// Enable Pointers
		// Enable Vertex Arrays
		glEnableClientState( GL_VERTEX_ARRAY );		
		glBindBufferARB( GL_ARRAY_BUFFER_ARB, node->GetVerticesName());
		// Set The Vertex Pointer To The Vertex Buffer
		glVertexPointer( 3, GL_FLOAT, 0, (char *) 0 );
	}
}
//-----------------------------------------------
/**
* Disables vertices
*/
void GLRenderer::DisableVertices(GeometryNode* node)
{
	if (node->GetVerticesName())
	{
		glDisableClientState( GL_VERTEX_ARRAY );
	}
}
//-----------------------------------------------
/**
* Enables normals
*/
void GLRenderer::EnableNormals(GeometryNode* node)
{
	if (node->GetNormalsName())
	{
		// Enable Normal Arrays
		glEnableClientState( GL_NORMAL_ARRAY );
		glBindBufferARB( GL_ARRAY_BUFFER_ARB, node->GetNormalsName());
		// Set The Vertex Pointer To The Normal Buffer
		glNormalPointer( GL_FLOAT, 0, (char *) 0 );
	}
}
//-----------------------------------------------
/**
* Disables normals
*/
void GLRenderer::DisableNormals(GeometryNode* node)
{
	if (node->GetNormalsName())
	{
		glDisableClientState( GL_NORMAL_ARRAY );
	}
}
//-----------------------------------------------
/**
* Enables tex coords
*/
void GLRenderer::EnableTexCoords(GeometryNode* node)
{
	if (node->GetTexCoordsName())
	{
		// Enable Texture Coord Arrays
		glEnableClientState( GL_TEXTURE_COORD_ARRAY );	
		glBindBufferARB( GL_ARRAY_BUFFER_ARB, node->GetTexCoordsName());
		// Set The TexCoord Pointer To The TexCoord Buffer
		glTexCoordPointer( 2, GL_FLOAT, 0, (char *) 0 );	
	}
}
//-----------------------------------------------
/**
* Disables tex coords
*/
void GLRenderer::DisableTexCoords(GeometryNode* node)
{
	if (node->GetTexCoordsName())
	{
		glDisableClientState( GL_TEXTURE_COORD_ARRAY );	
	}
}
//-----------------------------------------------
/**
* Enables textures
*/
void GLRenderer::EnableTextures(TextureProperty* tex)
{
	glBindTexture(GL_TEXTURE_2D, tex->GetId());
}
//-----------------------------------------------
/**
* Enables a material
*/
void GLRenderer::EnableMaterial(MaterialProperty* mat)
{	
	const mth::Vector3& ambient = mat->GetAmbient();
	const mth::Vector3& diffuse = mat->GetDiffuse();
	const mth::Vector3& specular = mat->GetSpecular();
	const mth::Vector3& emissive = mat->GetEmissive();
	float alpha = mat->GetAlpha();
	float ambientv[] = {ambient.x, ambient.y, ambient.z, 1.0f};
	float diffusev[] = {diffuse.x, diffuse.y, diffuse.z, alpha};
	float specularv[] = {specular.x, specular.y, specular.z, 1.0f};
	float emissivev[] = {emissive.x, emissive.y, emissive.z, 1.0f};
	float shininess = mat->GetShininess();
	glMaterialfv(GL_FRONT, GL_AMBIENT, ambientv);
	glMaterialfv(GL_FRONT, GL_DIFFUSE, diffusev);
	glMaterialfv(GL_FRONT, GL_SPECULAR, specularv);
	glMaterialfv(GL_FRONT, GL_EMISSION, emissivev);
	glMaterialf(GL_FRONT, GL_SHININESS, shininess);
}

//-----------------------------------------------
/**
* Enables lights for a node
*/
void GLRenderer::EnableLights(GeometryNode* node)
{
	const TransformNode::LightList& lights =
		node->GetLightList();
	
	glEnable(GL_LIGHTING);
	TransformNode::LightList::const_iterator it;
	unsigned int num = 0;
	for (it = lights.begin();
		it != lights.end();
		++it)
	{
		if ((*it)->IsOn())
			EnableLight((*it), num);
		if (++num > 8) return;
	}
}
//-----------------------------------------------
/**
* Enables one of 8 lights
*/
void GLRenderer::EnableLight(LightPtr light,
							 unsigned int num)
{
	const mth::Vector3& ambient = light->GetAmbient();
	const mth::Vector3& diffuse = light->GetDiffuse();
	const mth::Vector3& specular = light->GetSpecular();
	const mth::Vector3& pos = light->GetPosition();
	float ambientv[] = {ambient.x, ambient.y, ambient.z, 1.0f};
	float diffusev[] = {diffuse.x, diffuse.y, diffuse.z, 1.0f};
	float specularv[] = {specular.x, specular.y, specular.z, 1.0f};
	float posv[] = {pos.x, pos.y, pos.z, 1.0f};
	switch (num)
	{
	case 0:
		glLightfv(GL_LIGHT0, GL_AMBIENT, ambientv);
		glLightfv(GL_LIGHT0, GL_DIFFUSE, diffusev);
		glLightfv(GL_LIGHT0, GL_SPECULAR, specularv);
		glLightfv(GL_LIGHT0, GL_POSITION, posv);
		glEnable(GL_LIGHT0);
		break;
	case 1:
		glLightfv(GL_LIGHT1, GL_AMBIENT, ambientv);
		glLightfv(GL_LIGHT1, GL_DIFFUSE, diffusev);
		glLightfv(GL_LIGHT1, GL_SPECULAR, specularv);
		glLightfv(GL_LIGHT1, GL_POSITION, posv);
		glEnable(GL_LIGHT1);
		break;
	case 2:
		glLightfv(GL_LIGHT2, GL_AMBIENT, ambientv);
		glLightfv(GL_LIGHT2, GL_DIFFUSE, diffusev);
		glLightfv(GL_LIGHT2, GL_SPECULAR, specularv);
		glLightfv(GL_LIGHT2, GL_POSITION, posv);
		glEnable(GL_LIGHT2);
		break;
	case 3:
		glLightfv(GL_LIGHT3, GL_AMBIENT, ambientv);
		glLightfv(GL_LIGHT3, GL_DIFFUSE, diffusev);
		glLightfv(GL_LIGHT3, GL_SPECULAR, specularv);
		glLightfv(GL_LIGHT3, GL_POSITION, posv);
		glEnable(GL_LIGHT3);
		break;
	case 4:
		glLightfv(GL_LIGHT4, GL_AMBIENT, ambientv);
		glLightfv(GL_LIGHT4, GL_DIFFUSE, diffusev);
		glLightfv(GL_LIGHT4, GL_SPECULAR, specularv);
		glLightfv(GL_LIGHT4, GL_POSITION, posv);
		glEnable(GL_LIGHT4);
		break;
	case 5:
		glLightfv(GL_LIGHT5, GL_AMBIENT, ambientv);
		glLightfv(GL_LIGHT5, GL_DIFFUSE, diffusev);
		glLightfv(GL_LIGHT5, GL_SPECULAR, specularv);
		glLightfv(GL_LIGHT5, GL_POSITION, posv);
		glEnable(GL_LIGHT5);
		break;
	case 6:
		glLightfv(GL_LIGHT6, GL_AMBIENT, ambientv);
		glLightfv(GL_LIGHT6, GL_DIFFUSE, diffusev);
		glLightfv(GL_LIGHT6, GL_SPECULAR, specularv);
		glLightfv(GL_LIGHT6, GL_POSITION, posv);
		glEnable(GL_LIGHT6);
		break;
	case 7:
		glLightfv(GL_LIGHT7, GL_AMBIENT, ambientv);
		glLightfv(GL_LIGHT7, GL_DIFFUSE, diffusev);
		glLightfv(GL_LIGHT7, GL_SPECULAR, specularv);
		glLightfv(GL_LIGHT7, GL_POSITION, posv);
		glEnable(GL_LIGHT7);
		break;
	default:
		return;
	}
}
//-----------------------------------------------
/**
* Disables lighting
*/
void GLRenderer::DisableLights()
{
	glDisable(GL_LIGHT7);
	glDisable(GL_LIGHT6);
	glDisable(GL_LIGHT5);
	glDisable(GL_LIGHT4);
	glDisable(GL_LIGHT3);
	glDisable(GL_LIGHT2);
	glDisable(GL_LIGHT1);
	glDisable(GL_LIGHT0);

	glDisable(GL_LIGHTING);
}
//----------------------------------------------
/**
* Creates vertices, normals etc for a node
*/
bool GLRenderer::BindNodeToMesh(GeometryNode* node,
				    mth::Vector3* vertices,
					mth::Vector3* normals,
					mth::Vector2* texCoords,
					int numOfVertices)
{
	
	RenderStruct* renderStruct = new RenderStruct();
	// Get A Valid Name
	glGenBuffersARB( 1, &renderStruct->verticesName );		
	// Bind The Buffer
	glBindBufferARB( GL_ARRAY_BUFFER_ARB, renderStruct->verticesName );			
	// Load The Data
	glBufferDataARB( GL_ARRAY_BUFFER_ARB, 
		numOfVertices*sizeof(mth::Vector3), vertices, GL_STATIC_DRAW_ARB );

	if (normals)
	{
		// Get A Valid Name
		glGenBuffersARB( 1, &renderStruct->normalsName );		
		// Bind The Buffer
		glBindBufferARB( GL_ARRAY_BUFFER_ARB, renderStruct->normalsName );			
		// Load The Data
		glBufferDataARB( GL_ARRAY_BUFFER_ARB, 
			numOfVertices*sizeof(mth::Vector3), normals, GL_STATIC_DRAW_ARB );

	}

	if (texCoords)
	{
		// Generate And Bind The Texture Coordinate Buffer
		// Get A Valid Name
		glGenBuffersARB( 1, &renderStruct->texCoordsName );							
		// Bind The Buffer
		glBindBufferARB( GL_ARRAY_BUFFER_ARB, renderStruct->texCoordsName );		
		// Load The Data
		glBufferDataARB( GL_ARRAY_BUFFER_ARB, 
			numOfVertices*sizeof(mth::Vector2), texCoords, GL_STATIC_DRAW_ARB );

	}

	renderStruct->numOfVertices = numOfVertices;
	m_lRenderStructs.push_back(renderStruct);
	node->SetVerticesName(renderStruct->verticesName);
	node->SetNormalsName(renderStruct->normalsName);
	node->SetTexCoordsName(renderStruct->texCoordsName);
	node->SetNumOfVertices(renderStruct->numOfVertices);

	return true;
}

//----------------------------------------------
/**
* Debug code to show bounding boxes
*/
void GLRenderer::ShowBoundingBoxes(GeometryNode* node)
{

	glColor3f(1.0, 1.0, 1.0);
	const BoundingBox& b = node->GetBoundingBox();
	mth::Vector3 nearUpL(b.minX, b.maxY, b.maxZ);
	mth::Vector3 nearUpR(b.maxX, b.maxY, b.maxZ);
	mth::Vector3 nearDownL(b.minX, b.minY, b.maxZ);
	mth::Vector3 nearDownR(b.maxX, b.minY, b.maxZ);
	mth::Vector3 farUpL(b.minX, b.maxY, b.minZ);
	mth::Vector3 farUpR(b.maxX, b.maxY, b.minZ);
	mth::Vector3 farDownL(b.minX, b.minY, b.minZ);
	mth::Vector3 farDownR(b.maxX, b.minY, b.minZ);

	glBegin(GL_LINES);
	glVertex3f(nearUpL.x, nearUpL.y, nearUpL.z);
	glVertex3f(nearUpR.x, nearUpR.y, nearUpR.z);
	
	glVertex3f(nearUpR.x, nearUpR.y, nearUpR.z);
	glVertex3f(nearDownR.x, nearDownR.y, nearDownR.z);

	glVertex3f(nearDownR.x, nearDownR.y, nearDownR.z);
	glVertex3f(nearDownL.x, nearDownL.y, nearDownL.z);

	glVertex3f(nearDownL.x, nearDownL.y, nearDownL.z);
	glVertex3f(nearUpL.x, nearUpL.y, nearUpL.z);

	glVertex3f(farUpL.x, farUpL.y, farUpL.z);
	glVertex3f(farUpR.x, farUpR.y, farUpR.z);
	
	glVertex3f(farUpR.x, farUpR.y, farUpR.z);
	glVertex3f(farDownR.x, farDownR.y, farDownR.z);

	glVertex3f(farDownR.x, farDownR.y, farDownR.z);
	glVertex3f(farDownL.x, farDownL.y, farDownL.z);

	glVertex3f(farDownL.x, farDownL.y, farDownL.z);
	glVertex3f(farUpL.x, farUpL.y, farUpL.z);

	glVertex3f(farDownL.x, farDownL.y, farDownL.z);
	glVertex3f(nearDownL.x, nearDownL.y, nearDownL.z);

	glVertex3f(farDownR.x, farDownR.y, farDownR.z);
	glVertex3f(nearDownR.x, nearDownR.y, nearDownR.z);

	glVertex3f(farUpL.x, farUpL.y, farUpL.z);
	glVertex3f(nearUpL.x, nearUpL.y, nearUpL.z);

	glVertex3f(farUpR.x, farUpR.y, farUpR.z);
	glVertex3f(nearUpR.x, nearUpR.y, nearUpR.z);


	glEnd();
		
		
}
//----------------------------------------------
/**
* Tests if an AABB of a node
* is inside the view frustrum
*/
bool GLRenderer::IsVisible(TransformNode* node)
{
	const BoundingBox& b = node->GetBoundingBox();
	return b.IsInside(m_spCamera->GetFrustrum());
}
//----------------------------------------------
/**
* Creates a display list for the skybox
*/
void GLRenderer::SetSkyBox(SkyBoxPtr skyBox)
{
	m_spSkyBox = skyBox;
	if (m_uiSkyBoxDL)
	{
		glDeleteLists(m_uiSkyBoxDL, 1);
	}

	m_uiSkyBoxDL = glGenLists(1);
	float size2 = m_spSkyBox->GetSize() / 2.0f;
	if (m_spSkyBox->IsTextured())
	{
		unsigned int textures[6];
		for (int i=0; i<6; i++)
		{
			textures[i] = m_spSkyBox->GetTexture(i)->GetId();
		}
		
		// if sky box has more than one textures
		if (m_spSkyBox->HasMultipleTextures())
		{
			glNewList(m_uiSkyBoxDL, GL_COMPILE);

			glEnable(GL_TEXTURE_2D);
			// far plane
			glBindTexture(GL_TEXTURE_2D, textures[SkyBox::PLANE_FAR]);
			glBegin(GL_QUADS);
			glTexCoord2f(0.0f, 1.0f);
			glVertex3f(-size2, size2, -size2);
			glTexCoord2f(0.0f, 0.0f);
			glVertex3f(-size2, -size2, -size2);
			glTexCoord2f(1.0f, 0.0f);
			glVertex3f(size2, -size2, -size2);
			glTexCoord2f(1.0f, 1.0f);
			glVertex3f(size2, size2, -size2);
			glEnd();

			// near plane
			glBindTexture(GL_TEXTURE_2D, textures[SkyBox::PLANE_NEAR]);
			glBegin(GL_QUADS);
			glTexCoord2f(0.0f, 1.0f);
			glVertex3f(size2, size2, size2);
			glTexCoord2f(0.0f, 0.0f);
			glVertex3f(size2, -size2, size2);
			glTexCoord2f(1.0f, 0.0f);
			glVertex3f(-size2, -size2, size2);
			glTexCoord2f(1.0f, 1.0f);
			glVertex3f(-size2, size2, size2);
			glEnd();

			// top plane
			glBindTexture(GL_TEXTURE_2D, textures[SkyBox::PLANE_TOP]);
			glBegin(GL_QUADS);
			glTexCoord2f(0.0f, 1.0f);
			glVertex3f(-size2, size2, size2);
			glTexCoord2f(0.0f, 0.0f);
			glVertex3f(-size2, size2, -size2);
			glTexCoord2f(1.0f, 0.0f);
			glVertex3f(size2, size2, -size2);
			glTexCoord2f(1.0f, 1.0f);
			glVertex3f(size2, size2, size2);
			glEnd();

			// bottom plane
			glBindTexture(GL_TEXTURE_2D, textures[SkyBox::PLANE_BOTTOM]);
			glBegin(GL_QUADS);
			glTexCoord2f(0.0f, 1.0f);
			glVertex3f(-size2, -size2, -size2);
			glTexCoord2f(0.0f, 0.0f);
			glVertex3f(-size2, -size2, size2);
			glTexCoord2f(1.0f, 0.0f);
			glVertex3f(size2, -size2, size2);
			glTexCoord2f(1.0f, 1.0f);
			glVertex3f(size2, -size2, -size2);
			glEnd();

			// right plane
			glBindTexture(GL_TEXTURE_2D, textures[SkyBox::PLANE_RIGHT]);
			glBegin(GL_QUADS);
			glTexCoord2f(0.0f, 1.0f);
			glVertex3f(size2, size2, -size2);
			glTexCoord2f(0.0f, 0.0f);
			glVertex3f(size2, -size2, -size2);
			glTexCoord2f(1.0f, 0.0f);
			glVertex3f(size2, -size2, size2);
			glTexCoord2f(1.0f, 1.0f);
			glVertex3f(size2, size2, size2);
			glEnd();

			// left plane
			glBindTexture(GL_TEXTURE_2D, textures[SkyBox::PLANE_LEFT]);
			glBegin(GL_QUADS);
			glTexCoord2f(0.0f, 1.0f);
			glVertex3f(-size2, size2, size2);
			glTexCoord2f(0.0f, 0.0f);
			glVertex3f(-size2, -size2, size2);
			glTexCoord2f(1.0f, 0.0f);
			glVertex3f(-size2, -size2, -size2);
			glTexCoord2f(1.0f, 1.0f);
			glVertex3f(-size2, size2, -size2);
			glEnd();

			glDisable(GL_TEXTURE_2D);

			glEndList();
		}
		// skybox has one texture
		else
		{
			glNewList(m_uiSkyBoxDL, GL_COMPILE);

			glEnable(GL_TEXTURE_2D);
			// far plane
			glBindTexture(GL_TEXTURE_2D, textures[0]);
			glBegin(GL_QUADS);
			glTexCoord2f(0.0f, 1.0f);
			glVertex3f(-size2, size2, -size2);
			glTexCoord2f(0.0f, 0.0f);
			glVertex3f(-size2, -size2, -size2);
			glTexCoord2f(1.0f, 0.0f);
			glVertex3f(size2, -size2, -size2);
			glTexCoord2f(1.0f, 1.0f);
			glVertex3f(size2, size2, -size2);
			glEnd();

			// near plane
			glBegin(GL_QUADS);
			glTexCoord2f(0.0f, 1.0f);
			glVertex3f(size2, size2, size2);
			glTexCoord2f(0.0f, 0.0f);
			glVertex3f(size2, -size2, size2);
			glTexCoord2f(1.0f, 0.0f);
			glVertex3f(-size2, -size2, size2);
			glTexCoord2f(1.0f, 1.0f);
			glVertex3f(-size2, size2, size2);
			glEnd();

			// top plane
			glBegin(GL_QUADS);
			glTexCoord2f(0.0f, 1.0f);
			glVertex3f(-size2, size2, size2);
			glTexCoord2f(0.0f, 0.0f);
			glVertex3f(-size2, size2, -size2);
			glTexCoord2f(1.0f, 0.0f);
			glVertex3f(size2, size2, -size2);
			glTexCoord2f(1.0f, 1.0f);
			glVertex3f(size2, size2, size2);
			glEnd();

			// bottom plane
			glBegin(GL_QUADS);
			glTexCoord2f(0.0f, 1.0f);
			glVertex3f(-size2, -size2, -size2);
			glTexCoord2f(0.0f, 0.0f);
			glVertex3f(-size2, -size2, size2);
			glTexCoord2f(1.0f, 0.0f);
			glVertex3f(size2, -size2, size2);
			glTexCoord2f(1.0f, 1.0f);
			glVertex3f(size2, -size2, -size2);
			glEnd();

			// right plane
			glBegin(GL_QUADS);
			glTexCoord2f(0.0f, 1.0f);
			glVertex3f(size2, size2, -size2);
			glTexCoord2f(0.0f, 0.0f);
			glVertex3f(size2, -size2, -size2);
			glTexCoord2f(1.0f, 0.0f);
			glVertex3f(size2, -size2, size2);
			glTexCoord2f(1.0f, 1.0f);
			glVertex3f(size2, size2, size2);
			glEnd();

			// left plane
			glBegin(GL_QUADS);
			glTexCoord2f(0.0f, 1.0f);
			glVertex3f(-size2, size2, size2);
			glTexCoord2f(0.0f, 0.0f);
			glVertex3f(-size2, -size2, size2);
			glTexCoord2f(1.0f, 0.0f);
			glVertex3f(-size2, -size2, -size2);
			glTexCoord2f(1.0f, 1.0f);
			glVertex3f(-size2, size2, -size2);
			glEnd();

			glDisable(GL_TEXTURE_2D);

			glEndList();
		}
				
	}
	// skybox has no texture
	else
	{
		glNewList(m_uiSkyBoxDL, GL_COMPILE);

		glColor3f(m_vBackgroundColor.x, m_vBackgroundColor.y, m_vBackgroundColor.z);
		// far plane
		glBegin(GL_QUADS);
		glVertex3f(-size2, size2, -size2);
		glVertex3f(-size2, -size2, -size2);
		glVertex3f(size2, -size2, -size2);
		glVertex3f(size2, size2, -size2);
		glEnd();

		// near plane
		glBegin(GL_QUADS);
		glVertex3f(size2, size2, size2);
		glVertex3f(size2, -size2, size2);
		glVertex3f(-size2, -size2, size2);
		glVertex3f(-size2, size2, size2);
		glEnd();

		// top plane
		glBegin(GL_QUADS);
		glVertex3f(-size2, size2, size2);
		glVertex3f(-size2, size2, -size2);
		glVertex3f(size2, size2, -size2);
		glVertex3f(size2, size2, size2);
		glEnd();

		// bottom plane
		glBegin(GL_QUADS);
		glVertex3f(-size2, -size2, -size2);
		glVertex3f(-size2, -size2, size2);
		glVertex3f(size2, -size2, size2);
		glVertex3f(size2, -size2, -size2);
		glEnd();

		// right plane
		glBegin(GL_QUADS);
		glVertex3f(size2, size2, -size2);
		glVertex3f(size2, -size2, -size2);
		glVertex3f(size2, -size2, size2);
		glVertex3f(size2, size2, size2);
		glEnd();

		// left plane
		glBegin(GL_QUADS);
		glVertex3f(-size2, size2, size2);
		glVertex3f(-size2, -size2, size2);
		glVertex3f(-size2, -size2, -size2);
		glVertex3f(-size2, size2, -size2);
		glEnd();


		glEndList();
	}
}
//----------------------------------------------
/**
* Opengl.Org code: checks if VBO's are supported
*/
bool GLRenderer::IsVBOExtensionSupported()
{
	const unsigned char *pszExtensions = NULL;
	const unsigned char *pszStart;
	char* szTargetExtension = "GL_ARB_vertex_buffer_object";
	unsigned char *pszWhere, *pszTerminator;

	// Extension names should not have spaces
	pszWhere = (unsigned char *) strchr( szTargetExtension, ' ' );
	if( pszWhere || *szTargetExtension == '\0' )
		return false;

	// Get Extensions String
	pszExtensions = glGetString( GL_EXTENSIONS );

	// Search The Extensions String For An Exact Copy
	pszStart = pszExtensions;
	for(;;)
	{
		pszWhere = (unsigned char *) strstr( (const char *) pszStart, szTargetExtension );
		if( !pszWhere )
			break;
		pszTerminator = pszWhere + strlen( szTargetExtension );
		if( pszWhere == pszStart || *( pszWhere - 1 ) == ' ' )
			if( *pszTerminator == ' ' || *pszTerminator == '\0' )
				return true;
		pszStart = pszTerminator;
	}
	return false;
}

